package com.chen.blue.utils;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.http.HttpRequest;
import com.chen.blue.base.FileBase;
import com.chen.blue.sysconfig.SystemConfig;
import io.minio.*;
import lombok.Getter;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.io.Resource;
import org.springframework.http.HttpHeaders;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.net.URLEncoder;
import java.util.Date;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.CopyOnWriteArrayList;

/**
 * Created By CLT On 2023/10/28.
 */
@SuppressWarnings("all")
@Slf4j
public class MinioUtil {

    private static final SystemConfig systemConfig = SpringUtil.getBean(SystemConfig.class);

    private static FileBase fileBase = null;

    /**
     * 实现minio上的文件下载
     *
     * @param response
     * @param filePath
     */
    public static void download(HttpServletResponse response, String filePath) {

        OutputStream outputStream = null;
        GetObjectResponse object = null;
        long startTime = System.currentTimeMillis();

        try {
            log.info("文件下载中...");
            // 创建连接
            MinioClient client = MinioClient.builder().endpoint(systemConfig.getMinioEndpoint()).credentials(systemConfig.getMinioAccessKey(), systemConfig.getMinioSecretKey()).build();

            // 创建存储桶
            boolean isExists = client.bucketExists(BucketExistsArgs.builder().bucket(systemConfig.getMinioBucketName()).build());
            if (!isExists) {
                client.makeBucket(MakeBucketArgs.builder().bucket(systemConfig.getMinioBucketName()).build());
            }

            long minioTime = System.currentTimeMillis();
            object = client.getObject(GetObjectArgs.builder().bucket(systemConfig.getMinioBucketName()).object(filePath).build());

            StatObjectResponse statObjectResponse = client.statObject(StatObjectArgs.builder().bucket(systemConfig.getMinioBucketName()).object(filePath).build());
            long fileSize = statObjectResponse.size();
            log.info("文件大小：{}MB", fileSize / (1024 * 1024.0));

            long minioTimeEnd = System.currentTimeMillis();

            log.info("minio耗时：{}s", ((minioTimeEnd - minioTime) / 1000.0));

            int index = filePath.lastIndexOf("/");
            String fileName = filePath.substring(index + 1, filePath.length());

            // 清空response
            response.reset();
            // 设置response的Header
            response.setCharacterEncoding("UTF-8");
            //Content-Disposition的作用：告知浏览器以何种方式显示响应返回的文件，用浏览器打开还是以附件的形式下载到本地保存
            //attachment表示以附件方式下载   inline表示在线打开   "Content-Disposition: inline; filename=文件名.mp3"
            // filename表示文件的默认名称，因为网络传输只支持URL编码的相关支付，因此需要将文件名URL编码后进行传输,前端收到后需要反编码才能获取到真正的名称
            response.addHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(StrUtil.format("{}", fileName), "UTF-8"));
            // 告知浏览器文件的大小
            outputStream = response.getOutputStream();
            response.setContentType("application/octet-stream");
            response.setHeader("Accept-Ranges", "bytes");
            response.setHeader(HttpHeaders.CONTENT_LENGTH, String.valueOf(fileSize));


            long star = System.currentTimeMillis();
            int size = 1024 * 1024 * 50; // 50MB
            byte[] buffer = new byte[size];
            int temp = 0;
            while ((temp = object.read(buffer)) != -1) {
                outputStream.write(buffer, 0, temp);
            }

            outputStream.flush();
            log.info("IO：{}s", (System.currentTimeMillis() - star) / 1000.0);

            double needTime = (System.currentTimeMillis() - startTime) / 1000.0;
            log.info("文件下载总耗时：{}s", needTime);
        } catch (Exception ex) {
            ex.printStackTrace();
        } finally {
            try {

                if (outputStream != null)
                    outputStream.close();


                if (object != null)
                    object.close();

            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 本地资源下载
     *
     * @param response
     * @param resource
     */
    public static void download(HttpServletResponse response, Resource resource) {

        OutputStream outputStream = null;
        InputStream inputStream = null;
        long startTime = System.currentTimeMillis();

        try {
            log.info("文件下载中...");

            String fileName = resource.getFilename();
            inputStream = resource.getInputStream();
            int available = inputStream.available();

            // 清空response
            response.reset();
            // 设置response的Header
            response.setCharacterEncoding("UTF-8");
            //Content-Disposition的作用：告知浏览器以何种方式显示响应返回的文件，用浏览器打开还是以附件的形式下载到本地保存
            //attachment表示以附件方式下载   inline表示在线打开   "Content-Disposition: inline; filename=文件名.mp3"
            // filename表示文件的默认名称，因为网络传输只支持URL编码的相关支付，因此需要将文件名URL编码后进行传输,前端收到后需要反编码才能获取到真正的名称
            response.addHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(StrUtil.format("{}", fileName), "UTF-8"));
            // 告知浏览器文件的大小
            outputStream = response.getOutputStream();
            response.setContentType("application/octet-stream");
            response.setHeader("Accept-Ranges", "bytes");
            response.setHeader(HttpHeaders.CONTENT_LENGTH, String.valueOf(available));


            long star = System.currentTimeMillis();
            int size = 1024 * 1024 * 50; // 50MB
            byte[] buffer = new byte[size];
            int temp = 0;
            while ((temp = inputStream.read(buffer)) != -1) {
                outputStream.write(buffer, 0, temp);
            }

            outputStream.flush();
            log.info("IO：{}s", (System.currentTimeMillis() - star) / 1000.0);

            double needTime = (System.currentTimeMillis() - startTime) / 1000.0;
            log.info("文件下载总耗时：{}s", needTime);
        } catch (Exception ex) {
            ex.printStackTrace();
        } finally {
            try {

                if (outputStream != null)
                    outputStream.close();

                if (inputStream != null)
                    inputStream.close();

            } catch (IOException e) {
                e.printStackTrace();
            }
        }

    }


    /**
     * minio文件存储工具（普通上传）
     *
     * @param file
     * @return
     */
    public static FileBase upload(MultipartFile file, String moudle, Integer type) {

        long startTime = System.currentTimeMillis();
        log.info("文件上传中...");
        try {
            fileBase = new FileBase();

            // 创建连接
            MinioClient client = MinioClient.builder().endpoint(systemConfig.getMinioEndpoint()).credentials(systemConfig.getMinioAccessKey(), systemConfig.getMinioSecretKey()).build();

            // 创建存储桶
            boolean isExists = client.bucketExists(BucketExistsArgs.builder().bucket(systemConfig.getMinioBucketName()).build());
            if (!isExists) {
                client.makeBucket(MakeBucketArgs.builder().bucket(systemConfig.getMinioBucketName()).build());
            }

            // 文件处理
            String filename = file.getOriginalFilename();
            String objectName = StrUtil.format("{}/{}/{}/{}", moudle, DateUtil.format(new Date(), "yyyy/MM/dd"), System.currentTimeMillis(), filename);
            long size = file.getSize();

            fileBase.setUrl(objectName);
            fileBase.setName(filename);
            fileBase.setSize(size / (1024.0 * 1024.0));
            fileBase.setExtensionName(getExtensionName(filename));
            fileBase.setIsSuccess(Boolean.TRUE);
            fileBase.setType(type);

            // 存储
            long str = System.currentTimeMillis();
            client.putObject(PutObjectArgs.builder().bucket(systemConfig.getMinioBucketName()).object(objectName).stream(file.getInputStream(), size, -1).contentType(file.getContentType()).build());
            log.info("minio耗时：{}s", (System.currentTimeMillis() - str) / 1000.0);

            // 返回访问地址
            String picPath = StrUtil.format("{}/{}/{}", systemConfig.getMinioEndpoint(), systemConfig.getMinioBucketName(), objectName);
            log.info("上传的文件所在路径{}", picPath);

            log.info("文件上传总耗时：{}s", (System.currentTimeMillis() - startTime) / 1000.0);
            return fileBase;
        } catch (Exception e) {

            log.error("文件上传失败：{}", e.getMessage());
            fileBase.setIsSuccess(Boolean.FALSE);
            return fileBase;

        } finally {

            double size = fileBase.getSize();
            double speed = size / ((System.currentTimeMillis() - startTime) / 1000.0);
            log.info("文件上传平均速率：{}MB/s", speed);

        }

    }

    /**
     * minio文件存储工具（异步上传）
     *
     * @param file
     * @return
     */
    public static FileBase upload(FileBase fileBase, InputStream inputStream, String contentType) {

        long startTime = System.currentTimeMillis();
        log.info("文件上传中...");
        try {

            // 创建连接
            MinioClient client = MinioClient.builder().endpoint(systemConfig.getMinioEndpoint()).credentials(systemConfig.getMinioAccessKey(), systemConfig.getMinioSecretKey()).build();

            // 创建存储桶
            boolean isExists = client.bucketExists(BucketExistsArgs.builder().bucket(systemConfig.getMinioBucketName()).build());
            if (!isExists) {
                client.makeBucket(MakeBucketArgs.builder().bucket(systemConfig.getMinioBucketName()).build());
            }

            // 文件处理
            String filename = fileBase.getName();
            String objectName = fileBase.getUrl();
            long size = fileBase.getFileSize();


            // 存储
            long str = System.currentTimeMillis();
            client.putObject(PutObjectArgs.builder().bucket(systemConfig.getMinioBucketName()).object(objectName).stream(inputStream, size, -1).contentType(contentType).build());
            log.info("minio耗时：{}s", (System.currentTimeMillis() - str) / 1000.0);

            // 返回访问地址
            String picPath = StrUtil.format("{}/{}/{}", systemConfig.getMinioEndpoint(), systemConfig.getMinioBucketName(), objectName);
            log.info("上传的文件所在路径{}", picPath);

            log.info("文件上传总耗时：{}s", (System.currentTimeMillis() - startTime) / 1000.0);
            return fileBase;
        } catch (Exception e) {

            log.error("文件上传失败：{}", e.getMessage());
            fileBase.setIsSuccess(Boolean.FALSE);
            return fileBase;

        } finally {

            double size = fileBase.getSize();
            double speed = size / ((System.currentTimeMillis() - startTime) / 1000.0);
            log.info("文件上传平均速率：{}MB/s", speed);

        }

    }

    public static String getExtensionName(String fileName) {

        if (Objects.isNull(fileName) || StrUtil.isBlank(fileName))
            return null;

        int index = fileName.lastIndexOf(".");
        return fileName.substring(index + 1, fileName.length());

    }


}
